#include "MemLink.hpp"
#include "Futex.hpp"
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <cstring>
#include <thread>

bool MemLink::init(const std::string& devicePath) {
    fd_ = open(devicePath.c_str(), O_RDWR);
    shm_ = (SharedMemory*)mmap(nullptr, sizeof(SharedMemory), PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
    return shm_ != MAP_FAILED;
}

int MemLink::allocChannel(ChannelPermission perm) {
    std::lock_guard<std::mutex> lock(alloc_mutex_);
    for (int i = 0; i < MAX_CHANNELS; ++i) {
        uint32_t mask = 1 << i;
        if ((shm_->channel_bitmap.load() & mask) == 0) {
            shm_->channel_bitmap |= mask;
            shm_->channels[i].permission = perm;
            shm_->channels[i].futex_flag = 0;
            return i;
        }
    }
    return -1;
}

void MemLink::freeChannel(int channelId) {
    std::lock_guard<std::mutex> lock(alloc_mutex_);
    shm_->channel_bitmap &= ~(1 << channelId);
    shm_->channels[channelId].permission = ChannelPermission::NONE;
}

bool MemLink::write(int channelId, const std::vector<uint8_t>& data) {
    auto& ch = shm_->channels[channelId];
    if ((ch.permission & ChannelPermission::WRITE) == ChannelPermission::NONE) return false;
    if (ch.futex_flag.load() != 0) return false;

    ch.length = data.size();
    memcpy(ch.payload, data.data(), data.size());
    ch.futex_flag.store(1);
    futex_wake(&ch.futex_flag);
    return true;
}

void MemLink::onData(int channelId, std::function<void(const std::vector<uint8_t>&)> callback) {
    callbacks_[channelId] = callback;
}

void MemLink::run() {
    std::thread([this] {
        while (true) {
            for (auto& [id, cb] : callbacks_) {
                auto& ch = shm_->channels[id];
                if ((ch.permission & ChannelPermission::READ) == ChannelPermission::NONE) continue;

                if (ch.futex_flag.load() == 0) {
                    futex_wait(&ch.futex_flag, 0);
                }

                std::vector<uint8_t> data(ch.payload, ch.payload + ch.length);
                cb(data);
                ch.futex_flag.store(0);
            }
            usleep(1000);
        }
    }).detach();
}

void MemLink::shutdown() {
    munmap(shm_, sizeof(SharedMemory));
    close(fd_);
}
